package com.example.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.toolkit.AopUtils;
import com.example.mapper.master.StudentMapper;
import com.example.mapper.master.UserMapper;
import com.example.model.Student;
import com.example.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.util.List;

/**
 * 测试同一个类事务调用是否生效
 */
@Slf4j
@Service
public class TransactionalService {

    @Resource
    private UserMapper userMapper;

//    @Resource
//    private TransactionalService transactionalService;

    @Resource
    private StudentMapper studentMapper;

    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private PlatformTransactionManager masterTransactionManager;

    @Resource
    private TransactionalService2 transactionalService2;

    public String testTransactionInEffective(){

        // 新增数据,非事务方法调用事务方法，事务不生效
//        transactionalService.addData();

        return "success";
    }

    /**
     * 1：同一个事务时，插入的数据，包括user和student都会回滚
     * addData和addStudent方法传播机制都是Required，所以是同一个事务
     * 当插入student时报错，那么会走到addStudent方法的切面，处理回滚的逻辑，这时会给事务打上标记，onlyRollback，打完标记后异常向上抛出
     * 异常被外层的try-catch捕捉后，整个大的addData方法会走到提交commit，在处理commit的判断逻辑时，发现已经打了标记，所以整个大的事务
     * 只能被回滚
     *
     * 2：如果将addStudent方法的事务传播机制修改为Required_new,那么addStudent抛异常时只有student数据插不进去，
     * user数据还是可以插入的，因为addData和addStudent不是同一个事务，那么插Student时异常，直接会回滚，而不是打标。
     * 异常抛到完成，被捕捉到，那么不影响外层事务的提交
     */
    @Transactional(propagation=Propagation.REQUIRED)
    public void addUserAndStudent(){
        try{
            // 新增user对象
            User user = new User();
            user.setName("Lucy11111");
            user.setAge("28");
            userMapper.insert(user);

            // 新增student对象
            transactionalService2.addStudent();
//            AopUtils.getTargetObject(transactionalService2).addStudent();

        }catch (Exception e){
            log.error("新增user和student失败",e);
        }

    }

    @Transactional(timeout = 5)
    public String testTransactionTimeOut(){

        User user = new User();
        user.setId(1);
        user.setName("testTimeOut");
        userMapper.insert(user);

        // 睡眠5秒
        try {
            Thread.sleep(5000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return "success";

    }

    /**
     * 先睡眠，后插入数据
     */
    private void firstSleepLastInsert(){
        try {
            Thread.sleep(5000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        User user = new User();
        user.setId(1);
        user.setName("testTimeOut");
//        user.setCurrency("CNY");
        userMapper.insert(user);
    }

    private void firstInsertLastSleep(){
        User user = new User();
        user.setId(1);
        user.setName("testTimeOut");
//        user.setCurrency("CNY");
        userMapper.insert(user);

        try {
            Thread.sleep(5000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 只读事务readOnly目标是保证事务数据一致性，当方法内有多个查询时，加只读事务可以保证多个select查询
     * 使用同一个环境的数据（或者理解为事务开启时刻的数据）
     * @return
     */
    @Transactional(readOnly = true)
    public String testTransactionReadOnly(){

        List<User> userList = userMapper.selectList(new QueryWrapper<>());
        System.out.println("用户信息："+JSON.toJSONString(userList));


        List<Student> studentList = studentMapper.selectList(new QueryWrapper<>());
        System.out.println("学生信息："+JSON.toJSONString(studentList));

        return "success";
    }

}
